上面講的應該有些複雜
我會先把一些code打完, 並附上註解
之後會針對Express這個框架大概說明一下!
既然處理api/v1這個路徑這邊程式碼寫完之後
接著就是api/v1/auth/login/facebook這裡了
先去v1目錄底下新增一個index.js的檔案
v1/index.js
/*jslint node: true */
'use strict';
const express = require('express');
const router = express.Router();
// 將處理auth路徑相關的程式引入
const auth = require('./auth');
// 只要是api/v1/auth開頭的路徑都由auth.js處理
router.use('/auth', auth);
module.exports = router;
這時候我們幫app.js加上一個express-validator這個套件
像在開發的時候, 絕對要小心來自外部的資料
所以透過別人寫好的驗證器模組, 可以快速將惡意的資料格式阻擋
app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var validator = require('express-validator');
var index = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', index);
// 中間省略
module.exports = app;
接下來是v1/auth.js
/*jslint node: true */
'use strict';
const express = require('express');
const router = express.Router();
// 使用別人寫好的套件, 可以輕鬆取得FB上使用者的資料
const bluebird = require('bluebird');
const fbgraph = bluebird.promisifyAll(require('fbgraph'));
const _ = require('underscore');
const jwt = require('jsonwebtoken');
const db = require('../../models').getDatabase();
const USER_FIELD = {
_id: 1,
name: 1,
status: 1,
img: 1
};
const jwtConfig = {
// jwt的密鑰, 不可外流
secret: 'abcdQQWWEEE',
// 使用哪種演算法產生token
alg: 'HS512'
};
const config = {
jwtConfig: jwtConfig,
accessTokenConfig: {
algorithm: jwtConfig.alg,
expiresIn: "30d"
}
}
// router.post的意思就是指接受http post的方法
router.post('/login/facebook', async (req, res) => {
console.log('req.body', req.body);
req.checkBody('socialToken').notEmpty();
let errors = req.validationErrors();
// 如果有錯, 就回傳http error code 405
if (errors) {
return res.status(405).json({ err: errors });
}
let user, userIds;
// 要從Facebook中取得的欄位
let fields = {
fields: 'email,name,birthday,gender'
};
// 設定fbgraph中的使用者取得權杖
fbgraph.setAccessToken(req.body.socialToken);
try {
let fbProfile = await fbgraph.getAsync('me', fields);
// 如果沒有取得到使用者的生日性別跟名字, 就給個預設值
if (!fbProfile.birthday) {
fbProfile.birthday = 0;
}
if (!fbProfile.gender) {
fbProfile.gender = 'unset';
}
if (!fbProfile.name) {
fbProfile.name = 'guest';
}
// 設定查詢式
let query = {
socialId: 'facebook_' + fbProfile.id
};
let user = await db.collection('user').findOne(query, USER_FIELD);
if (!user) {
// 如果使用者不存在, 在資料庫新增一筆
let uConfig = {
status: 'active',
name: fbProfile.name,
email: fbProfile.email || fbProfile.id + '@still.not.set',
gender: fbProfile.gender,
img: 'https://graph.facebook.com/' + fbProfile.id + '/picture?type=large',
// 將字串的生日轉成毫秒數
birth: Date.parse(fbProfile.birthday),
// 使用者在Facebook上的ID
socialId: 'facebook_' + fbProfile.id,
// 使用者在Facebook上的token
socialToken: req.body.socialToken,
// 預留一個欄位, 以後可以擴充為使用帳號密碼
password: 'not_used',
// 來自於Facebook, 以後可擴充用
provider: 'facebook'
};
// socialId 必需設定為 unique (不可以重複)
let opResult = await db.collection('user').insertOne(uConfig, { j: true });
// 把user指向到uConfig這個物件
user = uConfig;
user._id = opResult.insertedId;
} else if (user.status !== 'active') {
return res.status(403).json({ err: '此用戶不存在或已被停權' });
} else {
// 使用者已經存在
// 每次使用者登入, 更新其Facebook權杖
if (req.body.socialToken) {
await db.collection('user').updateOne({
_id: user._id
}, {
$set: {
socialToken: req.body.socialToken
}
});
}
}
return res.json(issueTokenWithProfile(user));
} catch (e) {
console.error("Logging in using facebook:", e);
return res.status(500).json({ err: 'Login Err' });
}
});
// 將使用者部分的資訊放進去token中
function issueTokenWithProfile (user, accessTokenOnly=true) {
user.password = undefined;
user.socialToken = undefined;
user.socialId = undefined;
user.provider = undefined;
user.email = undefined;
user.status = undefined;
let payload = {
_id: user._id,
type: 'accessToken',
status: user.status
};
user.accessToken = jwt.sign(payload, config.jwtConfig.secret, config.accessTokenConfig);
return user;
};
module.exports = router;
下一篇會附上程式碼的壓縮檔 or github
然後大致說明一下 Express 的運作流程